// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "net/android/keystore_openssl.h"

#include <jni.h>
#include <openssl/bn.h>
#include <openssl/ec.h>
#include <openssl/engine.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/rsa.h>
#include <stdint.h>

#include <memory>

#include "base/android/build_info.h"
#include "base/android/scoped_java_ref.h"
#include "base/lazy_instance.h"
#include "base/logging.h"
#include "crypto/openssl_util.h"
#include "net/android/keystore.h"
#include "net/android/legacy_openssl.h"
#include "net/ssl/scoped_openssl_types.h"
#include "net/ssl/ssl_client_cert_type.h"

// IMPORTANT NOTE: The following code will currently only work when used
// to implement client certificate support with OpenSSL. That's because
// only the signing operations used in this use case are implemented here.
//
// Generally speaking, OpenSSL provides many different ways to sign
// digests. This code doesn't support all these cases, only the ones that
// are required to sign the digest during the OpenSSL handshake for TLS.
//
// The OpenSSL EVP_PKEY type is a generic wrapper around key pairs.
// Internally, it can hold a pointer to a RSA or ECDSA structure, which model
// keypair implementations of each respective crypto algorithm.
//
// The RSA type has a 'method' field pointer to a vtable-like structure
// called a RSA_METHOD. This contains several function pointers that
// correspond to operations on RSA keys (e.g. decode/encode with public
// key, decode/encode with private key, signing, validation), as well as
// a few flags.
//
// For example, the RSA_sign() function will call "method->rsa_sign()" if
// method->rsa_sign is not NULL, otherwise, it will perform a regular
// signing operation using the other fields in the RSA structure (which
// are used to hold the typical modulus / exponent / parameters for the
// key pair).
//
// This source file thus defines a custom RSA_METHOD structure whose
// fields point to static methods used to implement the corresponding
// RSA operation using platform Android APIs.
//
// However, the platform APIs require a jobject JNI reference to work. It must
// be stored in the RSA instance, or made accessible when the custom RSA
// methods are called. This is done by storing it in a |KeyExData| structure
// that's referenced by the key using |EX_DATA|.

using base::android::ScopedJavaGlobalRef;
using base::android::ScopedJavaLocalRef;

namespace net {
namespace android {

    namespace {

        extern const RSA_METHOD android_rsa_method;
        extern const ECDSA_METHOD android_ecdsa_method;

        // KeyExData contains the data that is contained in the EX_DATA of the RSA and
        // EC_KEY objects that are created to wrap Android system keys.
        struct KeyExData {
            // private_key contains a reference to a Java, private-key object.
            ScopedJavaGlobalRef<jobject> private_key;
            // legacy_rsa, if not NULL, points to an RSA* in the system's OpenSSL (which
            // might not be ABI compatible with Chromium).
            AndroidRSA* legacy_rsa;
            // cached_size contains the "size" of the key. This is the size of the
            // modulus (in bytes) for RSA, or the group order size for ECDSA. This
            // avoids calling into Java to calculate the size.
            size_t cached_size;
        };

        // ExDataDup is called when one of the RSA or EC_KEY objects is duplicated. We
        // don't support this and it should never happen.
        int ExDataDup(CRYPTO_EX_DATA* to,
            const CRYPTO_EX_DATA* from,
            void** from_d,
            int index,
            long argl,
            void* argp)
        {
            CHECK_EQ((void*)NULL, *from_d);
            return 0;
        }

        // ExDataFree is called when one of the RSA or EC_KEY objects is freed.
        void ExDataFree(void* parent,
            void* ptr,
            CRYPTO_EX_DATA* ad,
            int index,
            long argl,
            void* argp)
        {
            // Ensure the global JNI reference created with this wrapper is
            // properly destroyed with it.
            KeyExData* ex_data = reinterpret_cast<KeyExData*>(ptr);
            delete ex_data;
        }

        // BoringSSLEngine is a BoringSSL ENGINE that implements RSA and ECDSA by
        // forwarding the requested operations to the Java libraries.
        class BoringSSLEngine {
        public:
            BoringSSLEngine()
                : rsa_index_(RSA_get_ex_new_index(0 /* argl */,
                    NULL /* argp */,
                    NULL /* new_func */,
                    ExDataDup,
                    ExDataFree))
                , ec_key_index_(EC_KEY_get_ex_new_index(0 /* argl */,
                      NULL /* argp */,
                      NULL /* new_func */,
                      ExDataDup,
                      ExDataFree))
                , engine_(ENGINE_new())
            {
                ENGINE_set_RSA_method(engine_, &android_rsa_method,
                    sizeof(android_rsa_method));
                ENGINE_set_ECDSA_method(engine_, &android_ecdsa_method,
                    sizeof(android_ecdsa_method));
            }

            int rsa_ex_index() const { return rsa_index_; }
            int ec_key_ex_index() const { return ec_key_index_; }

            const ENGINE* engine() const { return engine_; }

        private:
            const int rsa_index_;
            const int ec_key_index_;
            ENGINE* const engine_;
        };

        base::LazyInstance<BoringSSLEngine>::Leaky global_boringssl_engine = LAZY_INSTANCE_INITIALIZER;

        // VectorBignumSize returns the number of bytes needed to represent the bignum
        // given in |v|, i.e. the length of |v| less any leading zero bytes.
        size_t VectorBignumSize(const std::vector<uint8_t>& v)
        {
            size_t size = v.size();
            // Ignore any leading zero bytes.
            for (size_t i = 0; i < v.size() && v[i] == 0; i++) {
                size--;
            }
            return size;
        }

        KeyExData* RsaGetExData(const RSA* rsa)
        {
            return reinterpret_cast<KeyExData*>(
                RSA_get_ex_data(rsa, global_boringssl_engine.Get().rsa_ex_index()));
        }

        size_t RsaMethodSize(const RSA* rsa)
        {
            const KeyExData* ex_data = RsaGetExData(rsa);
            return ex_data->cached_size;
        }

        int RsaMethodEncrypt(RSA* rsa,
            size_t* out_len,
            uint8_t* out,
            size_t max_out,
            const uint8_t* in,
            size_t in_len,
            int padding)
        {
            NOTIMPLEMENTED();
            OPENSSL_PUT_ERROR(RSA, RSA_R_UNKNOWN_ALGORITHM_TYPE);
            return 0;
        }

        int RsaMethodSignRaw(RSA* rsa,
            size_t* out_len,
            uint8_t* out,
            size_t max_out,
            const uint8_t* in,
            size_t in_len,
            int padding)
        {
            DCHECK_EQ(RSA_PKCS1_PADDING, padding);
            if (padding != RSA_PKCS1_PADDING) {
                // TODO(davidben): If we need to, we can implement RSA_NO_PADDING
                // by using javax.crypto.Cipher and picking either the
                // "RSA/ECB/NoPadding" or "RSA/ECB/PKCS1Padding" transformation as
                // appropriate. I believe support for both of these was added in
                // the same Android version as the "NONEwithRSA"
                // java.security.Signature algorithm, so the same version checks
                // for GetRsaLegacyKey should work.
                OPENSSL_PUT_ERROR(RSA, RSA_R_UNKNOWN_PADDING_TYPE);
                return 0;
            }

            // Retrieve private key JNI reference.
            const KeyExData* ex_data = RsaGetExData(rsa);
            if (!ex_data || !ex_data->private_key.obj()) {
                LOG(WARNING) << "Null JNI reference passed to RsaMethodSignRaw!";
                OPENSSL_PUT_ERROR(RSA, ERR_R_INTERNAL_ERROR);
                return 0;
            }

            // Pre-4.2 legacy codepath.
            if (ex_data->legacy_rsa) {
                int ret = ex_data->legacy_rsa->meth->rsa_priv_enc(
                    in_len, in, out, ex_data->legacy_rsa, ANDROID_RSA_PKCS1_PADDING);
                if (ret < 0) {
                    LOG(WARNING) << "Could not sign message in RsaMethodSignRaw!";
                    // System OpenSSL will use a separate error queue, so it is still
                    // necessary to push a new error.
                    //
                    // TODO(davidben): It would be good to also clear the system error queue
                    // if there were some way to convince Java to do it. (Without going
                    // through Java, it's difficult to get a handle on a system OpenSSL
                    // function; dlopen loads a second copy.)
                    OPENSSL_PUT_ERROR(RSA, ERR_R_INTERNAL_ERROR);
                    return 0;
                }
                *out_len = ret;
                return 1;
            }

            base::StringPiece from_piece(reinterpret_cast<const char*>(in), in_len);
            std::vector<uint8_t> result;
            // For RSA keys, this function behaves as RSA_private_encrypt with
            // PKCS#1 padding.
            if (!RawSignDigestWithPrivateKey(ex_data->private_key.obj(), from_piece,
                    &result)) {
                LOG(WARNING) << "Could not sign message in RsaMethodSignRaw!";
                OPENSSL_PUT_ERROR(RSA, ERR_R_INTERNAL_ERROR);
                return 0;
            }

            size_t expected_size = static_cast<size_t>(RSA_size(rsa));
            if (result.size() > expected_size) {
                LOG(ERROR) << "RSA Signature size mismatch, actual: " << result.size()
                           << ", expected <= " << expected_size;
                OPENSSL_PUT_ERROR(RSA, ERR_R_INTERNAL_ERROR);
                return 0;
            }

            if (max_out < expected_size) {
                OPENSSL_PUT_ERROR(RSA, RSA_R_DATA_TOO_LARGE);
                return 0;
            }

            // Copy result to OpenSSL-provided buffer. RawSignDigestWithPrivateKey
            // should pad with leading 0s, but if it doesn't, pad the result.
            size_t zero_pad = expected_size - result.size();
            memset(out, 0, zero_pad);
            memcpy(out + zero_pad, &result[0], result.size());
            *out_len = expected_size;

            return 1;
        }

        int RsaMethodDecrypt(RSA* rsa,
            size_t* out_len,
            uint8_t* out,
            size_t max_out,
            const uint8_t* in,
            size_t in_len,
            int padding)
        {
            NOTIMPLEMENTED();
            OPENSSL_PUT_ERROR(RSA, RSA_R_UNKNOWN_ALGORITHM_TYPE);
            return 0;
        }

        int RsaMethodVerifyRaw(RSA* rsa,
            size_t* out_len,
            uint8_t* out,
            size_t max_out,
            const uint8_t* in,
            size_t in_len,
            int padding)
        {
            NOTIMPLEMENTED();
            OPENSSL_PUT_ERROR(RSA, RSA_R_UNKNOWN_ALGORITHM_TYPE);
            return 0;
        }

        const RSA_METHOD android_rsa_method = {
            {
                0 /* references */, 1 /* is_static */
            } /* common */,
            nullptr /* app_data */,

            nullptr /* init */,
            nullptr /* finish */,
            RsaMethodSize,
            nullptr /* sign */,
            nullptr /* verify */,
            RsaMethodEncrypt,
            RsaMethodSignRaw,
            RsaMethodDecrypt,
            RsaMethodVerifyRaw,
            nullptr /* private_transform */,
            nullptr /* mod_exp */,
            nullptr /* bn_mod_exp */,
            RSA_FLAG_OPAQUE,
            nullptr /* keygen */,
            nullptr /* multi_prime_keygen */,
            nullptr /* supports_digest */,
        };

        // Setup an EVP_PKEY to wrap an existing platform RSA PrivateKey object.
        // |private_key| is the JNI reference (local or global) to the object.
        // |legacy_rsa|, if non-NULL, is a pointer to the system OpenSSL RSA object
        // backing |private_key|. This parameter is only used for Android < 4.2 to
        // implement key operations not exposed by the platform.
        // Returns a new EVP_PKEY on success, NULL otherwise.
        // On success, this creates a new global JNI reference to the object
        // that is owned by and destroyed with the EVP_PKEY. I.e. caller can
        // free |private_key| after the call.
        crypto::ScopedEVP_PKEY CreateRsaPkeyWrapper(
            jobject private_key,
            AndroidRSA* legacy_rsa,
            const crypto::OpenSSLErrStackTracer& tracer)
        {
            crypto::ScopedRSA rsa(RSA_new_method(global_boringssl_engine.Get().engine()));

            std::vector<uint8_t> modulus;
            if (!GetRSAKeyModulus(private_key, &modulus)) {
                LOG(ERROR) << "Failed to get private key modulus";
                return nullptr;
            }

            std::unique_ptr<KeyExData> ex_data(new KeyExData);
            ex_data->private_key.Reset(nullptr, private_key);
            if (ex_data->private_key.is_null()) {
                LOG(ERROR) << "Could not create global JNI reference";
                return nullptr;
            }
            ex_data->legacy_rsa = legacy_rsa;
            ex_data->cached_size = VectorBignumSize(modulus);

            RSA_set_ex_data(rsa.get(), global_boringssl_engine.Get().rsa_ex_index(),
                ex_data.release());

            crypto::ScopedEVP_PKEY pkey(EVP_PKEY_new());
            if (!pkey || !EVP_PKEY_set1_RSA(pkey.get(), rsa.get()))
                return nullptr;
            return pkey;
        }

        // On Android < 4.2, the libkeystore.so ENGINE uses CRYPTO_EX_DATA and is not
        // added to the global engine list. If all references to it are dropped, OpenSSL
        // will dlclose the module, leaving a dangling function pointer in the RSA
        // CRYPTO_EX_DATA class. To work around this, leak an extra reference to the
        // ENGINE we extract in GetRsaLegacyKey.
        //
        // In 4.2, this change avoids the problem:
        // https://android.googlesource.com/platform/libcore/+/106a8928fb4249f2f3d4dba1dddbe73ca5cb3d61
        //
        // https://crbug.com/381465
        class KeystoreEngineWorkaround {
        public:
            KeystoreEngineWorkaround() { }

            void LeakEngine(jobject private_key)
            {
                if (!engine_.is_null())
                    return;
                ScopedJavaLocalRef<jobject> engine = GetOpenSSLEngineForPrivateKey(private_key);
                if (engine.is_null()) {
                    NOTREACHED();
                    return;
                }
                engine_.Reset(engine);
            }

        private:
            ScopedJavaGlobalRef<jobject> engine_;
        };

        void LeakEngine(jobject private_key)
        {
            static base::LazyInstance<KeystoreEngineWorkaround>::Leaky s_instance = LAZY_INSTANCE_INITIALIZER;
            s_instance.Get().LeakEngine(private_key);
        }

        // Creates an EVP_PKEY wrapper corresponding to the RSA key
        // |private_key|. Returns nullptr on failure.
        crypto::ScopedEVP_PKEY GetRsaPkeyWrapper(jobject private_key)
        {
            const int kAndroid42ApiLevel = 17;
            crypto::OpenSSLErrStackTracer tracer(FROM_HERE);

            if (base::android::BuildInfo::GetInstance()->sdk_int() >= kAndroid42ApiLevel) {
                return CreateRsaPkeyWrapper(private_key, nullptr, tracer);
            }

            // Route around platform limitation: if Android < 4.2, then
            // base::android::RawSignDigestWithPrivateKey() cannot work, so try to get the
            // system OpenSSL's EVP_PKEY backing this PrivateKey object.
            AndroidEVP_PKEY* sys_pkey = GetOpenSSLSystemHandleForPrivateKey(private_key);
            if (sys_pkey == nullptr)
                return nullptr;

            if (sys_pkey->type != ANDROID_EVP_PKEY_RSA) {
                LOG(ERROR) << "Private key has wrong type!";
                return nullptr;
            }

            AndroidRSA* sys_rsa = sys_pkey->pkey.rsa;
            if (sys_rsa->engine) {
                // |private_key| may not have an engine if the PrivateKey did not come
                // from the key store, such as in unit tests.
                if (strcmp(sys_rsa->engine->id, "keystore") == 0) {
                    LeakEngine(private_key);
                } else {
                    NOTREACHED();
                }
            }

            return CreateRsaPkeyWrapper(private_key, sys_rsa, tracer);
        }

        // Custom ECDSA_METHOD that uses the platform APIs.
        // Note that for now, only signing through ECDSA_sign() is really supported.
        // all other method pointers are either stubs returning errors, or no-ops.

        jobject EcKeyGetKey(const EC_KEY* ec_key)
        {
            KeyExData* ex_data = reinterpret_cast<KeyExData*>(EC_KEY_get_ex_data(
                ec_key, global_boringssl_engine.Get().ec_key_ex_index()));
            return ex_data->private_key.obj();
        }

        size_t EcdsaMethodGroupOrderSize(const EC_KEY* ec_key)
        {
            KeyExData* ex_data = reinterpret_cast<KeyExData*>(EC_KEY_get_ex_data(
                ec_key, global_boringssl_engine.Get().ec_key_ex_index()));
            return ex_data->cached_size;
        }

        int EcdsaMethodSign(const uint8_t* digest,
            size_t digest_len,
            uint8_t* sig,
            unsigned int* sig_len,
            EC_KEY* ec_key)
        {
            // Retrieve private key JNI reference.
            jobject private_key = EcKeyGetKey(ec_key);
            if (!private_key) {
                LOG(WARNING) << "Null JNI reference passed to EcdsaMethodSign!";
                return 0;
            }
            // Sign message with it through JNI.
            std::vector<uint8_t> signature;
            base::StringPiece digest_sp(reinterpret_cast<const char*>(digest),
                digest_len);
            if (!RawSignDigestWithPrivateKey(private_key, digest_sp, &signature)) {
                LOG(WARNING) << "Could not sign message in EcdsaMethodSign!";
                return 0;
            }

            // Note: With ECDSA, the actual signature may be smaller than
            // ECDSA_size().
            size_t max_expected_size = ECDSA_size(ec_key);
            if (signature.size() > max_expected_size) {
                LOG(ERROR) << "ECDSA Signature size mismatch, actual: " << signature.size()
                           << ", expected <= " << max_expected_size;
                return 0;
            }

            memcpy(sig, &signature[0], signature.size());
            *sig_len = signature.size();
            return 1;
        }

        int EcdsaMethodVerify(const uint8_t* digest,
            size_t digest_len,
            const uint8_t* sig,
            size_t sig_len,
            EC_KEY* ec_key)
        {
            NOTIMPLEMENTED();
            OPENSSL_PUT_ERROR(ECDSA, ECDSA_R_NOT_IMPLEMENTED);
            return 0;
        }

        // Setup an EVP_PKEY to wrap an existing platform PrivateKey object.
        // |private_key| is the JNI reference (local or global) to the object.
        // Returns a new EVP_PKEY on success, NULL otherwise.
        // On success, this creates a global JNI reference to the object that
        // is owned by and destroyed with the EVP_PKEY. I.e. the caller shall
        // always free |private_key| after the call.
        crypto::ScopedEVP_PKEY GetEcdsaPkeyWrapper(jobject private_key)
        {
            crypto::OpenSSLErrStackTracer tracer(FROM_HERE);
            crypto::ScopedEC_KEY ec_key(
                EC_KEY_new_method(global_boringssl_engine.Get().engine()));

            std::vector<uint8_t> order;
            if (!GetECKeyOrder(private_key, &order)) {
                LOG(ERROR) << "Can't extract order parameter from EC private key";
                return nullptr;
            }

            std::unique_ptr<KeyExData> ex_data(new KeyExData);
            ex_data->private_key.Reset(nullptr, private_key);
            if (ex_data->private_key.is_null()) {
                LOG(ERROR) << "Can't create global JNI reference";
                return nullptr;
            }
            ex_data->legacy_rsa = nullptr;
            ex_data->cached_size = VectorBignumSize(order);

            EC_KEY_set_ex_data(ec_key.get(),
                global_boringssl_engine.Get().ec_key_ex_index(),
                ex_data.release());

            crypto::ScopedEVP_PKEY pkey(EVP_PKEY_new());
            if (!pkey || !EVP_PKEY_set1_EC_KEY(pkey.get(), ec_key.get()))
                return nullptr;
            return pkey;
        }

        const ECDSA_METHOD android_ecdsa_method = {
            {
                0 /* references */, 1 /* is_static */
            } /* common */,
            NULL /* app_data */,

            NULL /* init */,
            NULL /* finish */,
            EcdsaMethodGroupOrderSize,
            EcdsaMethodSign,
            EcdsaMethodVerify,
            ECDSA_FLAG_OPAQUE,
        };

    } // namespace

    crypto::ScopedEVP_PKEY GetOpenSSLPrivateKeyWrapper(jobject private_key)
    {
        // Create sub key type, depending on private key's algorithm type.
        PrivateKeyType key_type = GetPrivateKeyType(private_key);
        switch (key_type) {
        case PRIVATE_KEY_TYPE_RSA:
            return GetRsaPkeyWrapper(private_key);
        case PRIVATE_KEY_TYPE_ECDSA:
            return GetEcdsaPkeyWrapper(private_key);
        default:
            LOG(WARNING)
                << "GetOpenSSLPrivateKeyWrapper() called with invalid key type";
            return nullptr;
        }
    }

} // namespace android
} // namespace net
